package com.atguigu.tingshu.order.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.order.context.OrderHandlerInterceptorContext;
import com.atguigu.tingshu.order.context.OrderTradeStrategyContext;
import com.atguigu.tingshu.order.enumeration.OrderStatusEnum;
import com.atguigu.tingshu.order.enumeration.PayWayEnum;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.order.strategy.OrderTradeStrategy;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysql.cj.x.protobuf.MysqlxCrud;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper ;

    @Autowired
    private OrderDerateMapper orderDerateMapper ;

    @Autowired
    private KafkaService kafkaService ;


    @Override
    public OrderInfoVo trade(TradeVo tradeVo) {
        // log.info("OrderInfoServiceImpl...trade...方法执行了....");

        /**
         * 思路：需要根据付款项目的类型，构建OrderInfoVo对象，该对象中需要什么数据，就从数据库中查询什么数据。不同的付款项目类型执行的业务逻辑
         * 操作是不一样(查询的数据库表都不一样)
         */
//        OrderInfoVo orderInfoVo = new OrderInfoVo() ;
//        String itemType = tradeVo.getItemType();
//
//        if(SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
//
//            // 专辑购买
//
//        }else if(SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
//
//            // 声音购买
//
//        }else if(SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
//
//            // 购买vip
//
//        }

        /**
         * 通过if...else的判断是可以执行不同的代码逻辑，但是存在弊端：
         * 1、所有的代码写到一个方法中，就会导致该方法比较臃肿，不利于后期代码维护
         * 2、如果后期添加了新的专辑购买方式，那么此时就需要对该代码进行修改，不符合开闭原则(对扩展开发对修改关闭)
         *
         * 为了提供代码维护性和后期的扩展性，就需要想办法消除程序中if...else的代码。怎么消除呢? 需要使用策略设计模式
         *
         * 策略设计模式：
         * 策略模式的核心思想是对算法进行封装，委派给不同对象来管理。一种算法就是一个策略，最终就会形成很多的策略类，然后根据具体的情况选择不同的策略的类(算法)。
         *
         * 策略设计模式涉及到类：
         * 1、策略接口   -----> 定义算法的抽象
         * 2、策略实现类 -----> 一个策略接口的实现类就封装一种算法
         * 3、策略实现类的管理类  ----> 管理策略接口的实现类对象
         */
        OrderTradeStrategy orderTradeStrategy = OrderTradeStrategyContext.getOrderTradeStrategy(tradeVo.getItemType());
        OrderInfoVo orderInfoVo = orderTradeStrategy.trade(tradeVo);

        // 返回
        return orderInfoVo;
    }

    @Override
    public String submitOrder(OrderInfoVo orderInfoVo) {

        // 在保存的订单相关数据之前，需要做校验工作
        // 1、校验数据的合法性
        // 2、表单重复提交的校验
        // 3、保存订单的相关数据到对应的数据库表中(order_info , order_detail , order_derate)，然后返回订单编号

        /**
         * 如果把校验操作以及生成订单的操作都写在当前的方法中，那么就会导致该方法比较臃肿，影响了后期代码的维护性以及扩展性。
         * 并且还需要注意的是，如果某一个校验操作不通过，那么此时就不需要再执行后续的流程。
         *
         * 为了提高代码的维护性和后期的扩展性，那么此时就需要使用对应的设计模式进行改造。  -----> 本次需要使用的设计模式就是责任链设计模式。
         * 责任链模式顾名思义是将**不同职责的步骤串联起来执行，形成一个链表**，并且一个步骤执行完成之后才能够执行下一个步骤。同时需要给使用者提供
         * 一个入口，使用者只需要调用入口方法就可以依次执行责任链中的对应的任务。
         */
        OrderInfoVo orderInfo = OrderHandlerInterceptorContext.entry(orderInfoVo);

        // 返回订单编号
        return orderInfo.getTradeNo();
    }


    /**
     * 更改订单状态的操作：
     * 操作1：通过延迟任务更改订单的状态为已取消
     * 操作2：支付成功以后，需要将订单的状态改为已支付
     *
     * 操作1中的判断订单的状态和更改订单状态之间，操作2执行了。最终的问题：用户对订单支付成功了，但是订单的最终状态变成已取消。
     * 问题解决方案：加锁
     * 悲观锁：站在悲观的角度去看待问题，每当一个线程要对数据做修改的时候，总认为当前线程对数据修改的时候，其他的线程也会对数据进行操作，因此每一次对数据操作之前都需要加锁，保证多个线程
     *        对数据操作的互斥性（synchronized、Lock）。
     * 乐观锁：站在乐观的角度去看待问题，当一个线程对数据进行操作的时候，它认为当前线程对数据操作的时候，其他的线程不会对数据进行操作，因此每一次对数据进行操作之前不会进行加锁。当当前线程
     *        要对数据进行真正的修改的时候，需要判断当前线程对数据操作的时候其他的线程有没有对数据进行修改，如果没有直接修改数据；如果有，那么本次修改就是失败。
     *
     * 半杯水问题
     *
     * 悲观锁效率低于乐观锁。
     *
     */

    @Override
    public void orderCancel(String orderNo) {

        // 根据订单编号查询订单数据
        LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(OrderInfo::getOrderNo, orderNo);
        OrderInfo orderInfo = orderInfoMapper.selectOne(lambdaQueryWrapper);

        // 判断订单的状态
        if(SystemConstant.ORDER_STATUS_UNPAID.equals(orderInfo.getOrderStatus())) {

            // 把订单的状态改为已取消
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);

            // 在执行真实的数据库修改操作的时候，再次进行判断，判断当前订单的状态是否为未支付，如果是在执行真实的修改操作。
            // update order_info set order_status = '0903' where order_no = ? and order_status = '0901'
            lambdaQueryWrapper.eq(OrderInfo::getOrderStatus, SystemConstant.ORDER_STATUS_UNPAID);
            orderInfoMapper.update(orderInfo , lambdaQueryWrapper) ;

        }

    }

    @Override
    public void orderPaySuccess(String orderNo) {
        //修改订单的状态为已支付
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
        orderInfoMapper.updateById(orderInfo);

        //向kafka发送需要传递的消息消息的内容 orderNo userId itemIdList itemtype
        //因为是向user中发送kafka的消息 要查看user表中需要的数据
        Long userId = AuthContextHolder.getUserId();
        UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
        userPaidRecordVo.setOrderNo(orderNo);
        userPaidRecordVo.setUserId(userId);
        userPaidRecordVo.setItemType(orderInfo.getItemType());
        List<Long> itemIdList = orderInfo.getOrderDetailList().stream().map(orderDetail -> orderDetail.getItemId()).collect(Collectors.toList()); //查看orderDetail表 是不是包含两个id
        userPaidRecordVo.setItemIdList(itemIdList);
        kafkaService.sendMessage(KafkaConstant.QUEUE_USER_PAY_RECORD, JSON.toJSONString(userPaidRecordVo));
        log.info("发送保存交易信息消息成功...infoContent: {}" , JSON.toJSONString(userPaidRecordVo));



    }

    //根据交易号查询订单数据
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        // 创建LambdaQueryWrapper对象，封装查询条件
        LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(OrderInfo::getOrderNo , orderNo) ;
        OrderInfo orderInfo = orderInfoMapper.selectOne(lambdaQueryWrapper);

        // 查询订单所对应的明细数据
        LambdaQueryWrapper<OrderDetail> orderDetailLambdaQueryWrapper = new LambdaQueryWrapper<>() ;
        orderDetailLambdaQueryWrapper.eq(OrderDetail::getOrderId , orderInfo.getId()) ;
        List<OrderDetail> orderDetailList = orderDetailMapper.selectList(orderDetailLambdaQueryWrapper);
        orderInfo.setOrderDetailList(orderDetailList);

        // 查询订单的减免明细数据
        LambdaQueryWrapper<OrderDerate> derateLambdaQueryWrapper = new LambdaQueryWrapper<>() ; //查询订单
        derateLambdaQueryWrapper.eq(OrderDerate::getOrderId , orderInfo.getId()) ;
        List<OrderDerate> orderDerateList = orderDerateMapper.selectList(derateLambdaQueryWrapper);
        orderInfo.setOrderDerateList(orderDerateList) ;

        // 设置状态的名称 分为三种：未支付 、已支付 、已取消
        //orderInfo.setOrderStatusName(OrderStatusEnum.getOrderStatusName(orderInfo.getOrderStatus()));
          orderInfo.setOrderStatusName(OrderStatusEnum.getOrderStatusName(orderInfo.getOrderStatus()));

        // 设置支付方式的名称
        orderInfo.setPayWayName(PayWayEnum.getPayWayName(orderInfo.getPayWay()));

        // 返回
        return orderInfo;

    }

    @Override
    public IPage<OrderInfo> findOrderInfoByPage(Integer pageNo, Integer pageSize) {
        //获取登录用户的id
        Long userId = AuthContextHolder.getUserId();
        LambdaQueryWrapper<OrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(OrderInfo::getUserId,userId);

        Page<OrderInfo> page = new Page<>(pageNo,pageSize);
        this.page(page,lambdaQueryWrapper);
        //还没有获取我们的订单的详细数据
        page.getRecords().stream().forEach(orderInfo -> {
            Long orderId = orderInfo.getId();
            LambdaQueryWrapper<OrderDetail> detailLambdaQueryWrapper = new LambdaQueryWrapper<>();
            detailLambdaQueryWrapper.eq(OrderDetail::getOrderId,orderId);
            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(detailLambdaQueryWrapper);
            orderInfo.setOrderDetailList(orderDetailList);

            orderInfo.setOrderStatusName(OrderStatusEnum.getOrderStatusName(orderInfo.getOrderStatus()));
            orderInfo.setPayWay(PayWayEnum.getPayWayName(orderInfo.getPayWay()));
        });

        return page;
    }
}







































